package com.test.redis.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.test.redis.base.BizException;
import com.test.redis.dto.ReservationConverter;
import com.test.redis.dto.ReservationRecordDTO;
import com.test.redis.dto.ReservationRequest;
import com.test.redis.entity.MeetingRoom;
import com.test.redis.entity.ReservationRecord;
import com.test.redis.mapper.MeetingRoomMapper;
import com.test.redis.mapper.ReservationRecordMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 会议室预定服务
 */
@Service
@Slf4j
public class ReservationService {

    @Autowired
    private MeetingRoomService meetingRoomService;
    @Autowired
    private MeetingRoomMapper meetingRoomMapper;
    @Autowired
    private ReservationRecordMapper reservationRecordMapper;
    @Autowired
    private ReservationBitMapService bitMapService;
    @Autowired
    private ReservationConverter reservationConverter;

    /**
     * 预定会议室
     */
    @Transactional(rollbackFor = Exception.class)
    public ReservationRecordDTO reserve(ReservationRequest request, Long userId, String userName) {
        // 参数校验
        validateReservationRequest(request);

        // 检查会议室是否存在且可用
        MeetingRoom room = meetingRoomService.getById(request.getRoomId());
        if (room == null) {
            throw new BizException("会议室不存在");
        }
        if (room.getStatus() == 0) {
            throw new BizException("会议室已禁用");
        }

        // 使用改进的Redis Bitmap检查时间段是否可用（15分钟粒度）
        if (!bitMapService.isTimeSlotAvailable(request.getRoomId(), request.getStartTime(), request.getEndTime())) {
            throw new BizException("该时间段已被预定");
        }

        // 数据库层面再次检查冲突（双重检查确保数据一致性）
        List<ReservationRecord> conflicts = reservationRecordMapper.findConflictReservations(
                request.getRoomId(), request.getStartTime(), request.getEndTime());
        if (!conflicts.isEmpty()) {
            throw new BizException("时间段冲突，预定失败");
        }

        // 创建预定记录
        ReservationRecord record = reservationConverter.toEntity(request);
        record.setUserId(userId);
        record.setUserName(userName);
        record.setStatus(1); // 预定中
        record.setCreateTime(LocalDateTime.now());
        record.setUpdateTime(LocalDateTime.now());

        reservationRecordMapper.insert(record);

        // 在Redis中设置预定状态
        boolean success = bitMapService.reserveTimeSlot(
                request.getRoomId(), request.getStartTime(), request.getEndTime(), record.getId().toString());

        if (!success) {
            throw new BizException("预定失败，请重试");
        }

        log.info("会议室预定成功：roomId={}, userId={}, time={}~{}",
                request.getRoomId(), userId, request.getStartTime(), request.getEndTime());

        return reservationConverter.toDTO(record, room);
    }

    /**
     * 校验预定请求参数（增强校验）
     */
    private void validateReservationRequest(ReservationRequest request) {
        if (request.getStartTime().isAfter(request.getEndTime())) {
            throw new BizException("开始时间不能晚于结束时间");
        }

        if (request.getStartTime().isBefore(LocalDateTime.now())) {
            throw new BizException("不能预定过去的时间");
        }

        // 预定时长不能超过8小时
        if (Duration.between(request.getStartTime(), request.getEndTime()).toHours() > 8) {
            throw new BizException("单次预定时长不能超过8小时");
        }

        // 开始时间和结束时间必须对齐到15分钟
        if (request.getStartTime().getMinute() % 15 != 0 || request.getStartTime().getSecond() != 0 || request.getStartTime().getNano() != 0) {
            throw new BizException("开始时间必须对齐到15分钟（如09:00, 09:15, 09:30等）");
        }

        if (request.getEndTime().getMinute() % 15 != 0 || request.getEndTime().getSecond() != 0 || request.getEndTime().getNano() != 0) {
            throw new BizException("结束时间必须对齐到15分钟（如09:00, 09:15, 09:30等）");
        }

        // 最短预定时间15分钟
        if (Duration.between(request.getStartTime(), request.getEndTime()).toMinutes() < 15) {
            throw new BizException("预定时间最短为15分钟");
        }
    }

    /**
     * 查询会议室可用时间段（使用改进的方法）
     */
    public List<ReservationBitMapService.TimeSlot> getAvailableSlots(Long roomId, LocalDate date) {
        return bitMapService.getDailyAvailableSlots(roomId, date);
    }

    /**
     * 批量查询多个会议室的可用性
     */
    public Map<Long, Boolean> batchCheckAvailability(List<Long> roomIds, LocalDateTime startTime, LocalDateTime endTime) {
        return bitMapService.batchCheckAvailability(roomIds, startTime, endTime);
    }

    /**
     * 查询用户预定记录
     */
    public List<ReservationRecordDTO> getUserReservations(Long userId) {
        LambdaQueryWrapper<ReservationRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ReservationRecord::getUserId, userId)
                .orderByDesc(ReservationRecord::getCreateTime);

        List<ReservationRecord> records = reservationRecordMapper.selectList(queryWrapper);
        return records.stream().map(record -> {
            MeetingRoom room = meetingRoomMapper.selectById(record.getRoomId());
            return reservationConverter.toDTO(record, room);
        }).collect(Collectors.toList());
    }

    /**
     * 根据ID获取预定记录 - 使用缓存
     */
    public ReservationRecord getReservationById(Long id) {
        log.info("查询数据库获取预定记录，id: {}", id);
        return reservationRecordMapper.selectById(id);
    }

    /**
     * 取消预定 - 清除缓存
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelReservation(Long reservationId, Long userId) {
        ReservationRecord record = getReservationById(reservationId);
        if (record == null) {
            throw new BizException("预定记录不存在");
        }

        if (!record.getUserId().equals(userId)) {
            throw new BizException("只能取消自己的预定");
        }

        if (record.getStatus() != 1) {
            throw new BizException("只能取消进行中的预定");
        }

        // 更新数据库状态
        record.setStatus(0); // 已取消
        record.setUpdateTime(LocalDateTime.now());
        reservationRecordMapper.updateById(record);

        // 清除Redis中的预定状态
        bitMapService.cancelTimeSlot(record.getRoomId(), record.getStartTime(), record.getEndTime());

        log.info("取消预定成功：reservationId={}, userId={}", reservationId, userId);
    }
}